package cn.percent.factory;

import cn.percent.annotation.Autowired;
import cn.percent.annotation.Service;
import cn.percent.annotation.Transactional;
import com.alibaba.druid.util.StringUtils;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.reflections.Reflections;

import java.io.File;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.util.*;

public class BeanFactory {
    private static Map<String,Object> map = new HashMap<>();
    static {
        // 解析Service注解，利用反射实例化
        Reflections reflections = new Reflections("cn.percent");
        Set<Class<?>> typesAnnotatedSet = reflections.getTypesAnnotatedWith(Service.class);
//        System.out.println(typesAnnotatedSet);
        for (Class<?> aClass : typesAnnotatedSet) {
            try {
                Object beanObject = aClass.newInstance();
                Service annotation = aClass.getAnnotation(Service.class);
                String beanId = annotation.value();
//                System.out.println(beanId);
                // 判断beanId是否为空，如果为空按照类名首字母小写作为beanId
                if(StringUtils.isEmpty(beanId)){
                    beanId = toLowerCaseFirstOne(aClass.getSimpleName());
                }
                map.put(beanId,beanObject);
                //System.out.println(map);
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        // 解析Autowired
        try{
            for (Class<?> aClass : typesAnnotatedSet) {
                //System.out.println(aClass);
                Field[] declaredField = aClass.getDeclaredFields();
                //System.out.println(declaredField);
                for (Field field : declaredField) {
                    Autowired annotation = field.getAnnotation(Autowired.class);
                    if (annotation != null && annotation.required()){
                        String childrenId = null; // 需要注入的类的id
                        Object children = null; // 需要注入的类的实例
                        Class<?> type = field.getType();
                        System.out.println(type);
                        if (type.isInterface()){
                            System.out.println(type);
                            // 实现接口的所有类
                            List<Class> allInterfaceAchieveClass = getAllInterfaceAchieveClass(type);
                            System.out.println(allInterfaceAchieveClass);
                            Class<?> childrenType = allInterfaceAchieveClass.get(0);
                            if (childrenType.isAnnotationPresent(Service.class)) {
                                String implementationClassValue = childrenType.getAnnotation(Service.class).value();
                                childrenId = StringUtils.isEmpty(implementationClassValue) ? toLowerCaseFirstOne(type.getSimpleName()) : implementationClassValue;

                            }
                        } else {
                            childrenId = field.getName();
                        }
                        children = map.get(childrenId);
                        String parentAnnotationValue = aClass.getAnnotation(Service.class).value();
                        String parentId = StringUtils.isEmpty(parentAnnotationValue) ? toLowerCaseFirstOne(aClass.getSimpleName()) : parentAnnotationValue;
                        Object parent = map.get(parentId);
                        Method[] methods = aClass.getMethods();
                        for (int j=0;j<methods.length;j++){
                            Method method = methods[j];
                            if(method.getName().equalsIgnoreCase("set" + childrenId)){
                                method.invoke(parent,children);
                            }
                        }
                        map.put(parentId,parent);
                    }
                }

            }
        } catch (Exception e){
            e.printStackTrace();
        }

        // 解析@Transaction
        ProxyFactory proxyFactory = (ProxyFactory) map.get("proxyFactory");
        for (Class<?> aClass : typesAnnotatedSet) {
            boolean annotationPresent = aClass.isAnnotationPresent(Transactional.class);
            if(annotationPresent) {
                Service annotation = aClass.getAnnotation(Service.class);
                String serviceId = annotation.value();
                // 获取委托对象
                String delegateId = StringUtils.isEmpty(serviceId) ? toLowerCaseFirstOne(aClass.getSimpleName()) : serviceId;
                Object delegateObject = map.get(delegateId);
                Class<?>[] interfaces = aClass.getInterfaces();
                if (interfaces.length > 0) {
                    Object jdkProxy = proxyFactory.getJDKProxy(delegateObject);
                    map.put(delegateId,jdkProxy);
                } else {
                    Object cglibProxy = proxyFactory.getCglibProxy(delegateObject);
                    map.put(delegateId,cglibProxy);
                }

            }
        }


    }
    /*
    * 首字母转换小写
    * */
    private static String toLowerCaseFirstOne(String s){
        if (Character.isLowerCase(s.charAt(0))){
            return s;
        } else {
            return (new StringBuilder()).append(Character.toLowerCase(s.charAt(0))).append(s.substring(1)).toString();
        }
    }
    /*
     * 获取所有接口实现类
     * */
    public static List<Class> getAllInterfaceAchieveClass(Class clazz){
        ArrayList<Class> list  = new ArrayList<>();
        if(clazz.isInterface()){
            try{

                ArrayList<Class> allClassByPath = getAllClassByPath(clazz.getPackage().getName());
                System.out.println(allClassByPath);
                for (int i=0;i<allClassByPath.size();i++){
                    // 排除抽象类
//                    System.out.println(allClassByPath.get(i));
//                    System.out.println(allClassByPath.get(i).getModifiers());
//                    System.out.println(Modifier.isAbstract(allClassByPath.get(i).getModifiers()));
//                    System.out.println(Modifier.isInterface(allClassByPath.get(i).getModifiers()));
                    // cn.percent.dao.AccountDao 执行getModifiers()方法返回1537  ？？？？存疑
                    int modifiers = allClassByPath.get(i).getModifiers();
                    if (Modifier.isAbstract(modifiers)){
                        continue;
                    }
                    if (Modifier.isInterface(modifiers)){
                        continue;
                    }
                    //System.out.println(allClassByPath.get(i));
                    // 判断是否实现了接口
                    if(clazz.isAssignableFrom(allClassByPath.get(i))) {
                        list.add(allClassByPath.get(i));
                    }
                }
            } catch (Exception e){
                e.printStackTrace();
            }
        }
        return list;
    }

    /*
     * 获取指定路径下所有类
     * */
    public static ArrayList<Class> getAllClassByPath(String packageName){
        ArrayList<Class> list = new ArrayList<>();
        ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
        String path = packageName.replace(".", "/");

        try{
            ArrayList<File> fileList = new ArrayList<>();
            Enumeration<URL> enumeration = contextClassLoader.getResources(path);
            while(enumeration.hasMoreElements()) {
                URL url = enumeration.nextElement();
                fileList.add(new File(url.getFile()));
            }
            for(int i=0;i<fileList.size();i++) {
                list.addAll(findClass(fileList.get(i),packageName));
            }

        } catch (Exception e){
            e.printStackTrace();
        }
        return list;
    }

    /*
     * 如果file是目录，则递归调用findClass，获取目录下的类
     * 如果file是文件，则加入list中进行保存
     * */
    private static ArrayList<Class> findClass(File file, String packageName){
        ArrayList<Class> list = new ArrayList<>();
        if(!file.exists()){
            return list;
        }
        File[] files = file.listFiles();
        for (File fileIns : files) {
            if (fileIns.isDirectory()){
                assert !fileIns.getName().contains(".");
                ArrayList<Class> aClassList = findClass(fileIns, packageName + "." + fileIns.getName());
                list.addAll(aClassList);
            } else if(fileIns.getName().endsWith(".class")) {
                try{
                    list.add(Class.forName(packageName + "." + fileIns.getName().substring(0,fileIns.getName().length() - 6)));

                } catch (Exception e){
                    e.printStackTrace();
                }
            }
        }
        return list;
    }
    public static Object getBean(String id) {
        return map.get(id);
    }
}
